package com.example.boot.aop;

import com.example.common.LogAnno;
import com.example.context.BaseContext;
import com.example.pojo.entity.Account;
import com.example.pojo.entity.Admin;
import com.example.pojo.entity.OperateLog;
import com.example.service.AdminService;
import com.example.service.OperateLogService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.jupiter.api.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * TODO
 *
 * @author ding
 * @since 2024/6/26
 */
@Component
@Aspect
@Order(3)
public class LogAspect {

    @Autowired(required = true)
    private OperateLogService operateLogService;

    @Autowired
    private AdminService adminService;

    private OperateLog operateLog;

    // 默认允许插值
    private boolean flag = true;

    /**
     * 配置织入点
     *
     * @annotation方式是指：切入点 是指定作用于方法上的注解，即被Spring扫描到方法上带有该注解 就会执行切面通知。
     */
    @Pointcut("@annotation(com.example.common.LogAnno)")
    public void logPointCut() {
    }

    @Transactional
    @Around("logPointCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
        operateLog = new OperateLog();
        Object proceed = null;

        //获取方法名称
        String methodName = joinPoint.getSignature().getName();
        methodName = methodName.substring(methodName.lastIndexOf(".")+1);
        //获取参数列表
        Object[] args = joinPoint.getArgs();

        //获得注解
        LogAnno controllerLog = getAnnotationLog(joinPoint);
        if (controllerLog.isSaveRequestData()) {
            // 设置 operateLog属性
            operateLog.setTitle(controllerLog.title());
            operateLog.setType(controllerLog.type());
            operateLog.setCreateTime(LocalDateTime.now());
            operateLog.setMethod(methodName);

            //Account(id=null, username=admin, name=null, password=123456, role=null, avatar=null, phone=null, email=null, description=null, address=null, newPassword=null, token=null, teamName=null)
            String param = args[0].toString();
            if (param.length() > 2) {
                String[]params = getParamsArray(param);
                param = param.substring(param.indexOf("(")+1,param.lastIndexOf(")"));
                StringBuilder sb = new StringBuilder("");
                sb.append("{ ");
                for (String str : params) {
                    String[] s = str.split("=");
                    if (s.length > 1 && !s[1].equals("null")){
                        sb.append(str).append(" ");
                    }
                }
                sb.append("}");
                operateLog.setParams(sb.toString());

                if (params.length == 0){
                    flag = false;
                }
            } else {
                param = "{" + param + "}";
                operateLog.setParams(param);
            }


            // 获取HttpServletRequest
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = servletRequestAttributes.getRequest();

            // 获取http请求方法
            operateLog.setHttpMethod(request.getMethod());
            // 获取url地址
            operateLog.setUrl(request.getRequestURI());
            if (!methodName.equals("login") && !methodName.equals("register")) {
                Admin admin = adminService.selectById(Math.toIntExact(BaseContext.getCurrentId()));
                Object username = admin.getUsername();
                operateLog.setOperator(username.toString());
            } else if (methodName.equals("login")){
                String[] params = getParamsArray(param);
                String opearte = params[1];
                operateLog.setOperator(opearte.split("=")[1]);
            } else {
                operateLog.setOperator("没有该用户");
            }

            //调用原始方法执行
            try{
                proceed = joinPoint.proceed(args);
            }catch(Exception e){
                e.printStackTrace();
                //handleLog(joinPoint, e);
            }

            //handleLog(joinPoint, null);
        }

        return proceed;
    }

    /**
     * 拦截异常操作
     */
    /*@AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e);
    }*/

    protected void handleLog(final JoinPoint joinPoint, final Exception e) {
        try {
            if (getAnnotationLog(joinPoint).isSaveRequestData()) {
                // 获取方法所在的类名
                String className = joinPoint.getTarget().getClass().getName();
                String methodName = joinPoint.getSignature().getName();
                String methodStr = className + "." + methodName + "()";
                operateLog.setMethod(methodStr);

                // 获取异常信息
                if (e != null) {
                    operateLog.setOperateResult(StringUtils.substring(e.getMessage(), 0, 2000));
                }
                operateLog.setOperateResult("success");

                //if (flag) {
                    operateLogService.insertLog(operateLog);
                //} else {
                    //flag = true;
                //}
            }
        } catch (Exception exp) {
            exp.printStackTrace();
        }
    }

    /**
     * 获取存在的注解
     */
    private LogAnno getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(LogAnno.class);
        }
        return null;
    }

    /**
     * 将请求参数转换为字符数组
     */
    public String[] getParamsArray(String param) {
        return param.split(",");
    }


}
